/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_provisioning.h"

#include <stdlib.h>
#include <stdbool.h>

#include "ola_data.h"
#include "ola_device_model.h"
#include "ola_error.h"
#include "ola_log.h"
#include "ola_pal_os.h"
#include "ola_provisioning_softap.h"
#include "ola_sdk.h"
#include "securec.h"

static OlaNetCfgParam g_netCfgParam;
static bool g_isInit = false;
static const OlaProvisioningHooks *g_proHooks = NULL;

static int RegisterProvisionHooks(const OlaProvisioningHooks *hook)
{
    if ((hook == NULL) || (hook->start == NULL) || (hook->stop == NULL)) {
        OlaLogError("invalid provisioning hook");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    g_proHooks = hook;
    return OLA_OK;
}

static void UnRegisterProvisioningHooks(void)
{
    g_proHooks = NULL;
}

static int IsNeedProvisioning(bool *flag)
{
    if (flag == NULL) {
        OlaLogError("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    /* 获取设备配网类型、判断是否已经注册 */
    if ((g_netCfgParam.protocol == OLA_NETCFG_PROTOCOL_NONE) || OlaIsRegister()) {
        *flag = false;
        return OLA_OK;
    }
    *flag = true;
    return OLA_OK;
}

static int ProvisioningHooksInit(void)
{
    /* 获取设备配网类型，判断配网方式 */
    OlaNetcfgProtocol cfgProtocol = g_netCfgParam.protocol;
    const OlaProvisioningHooks *hooks = NULL;
    switch (cfgProtocol) {
        case OLA_NETCFG_PROTOCOL_TYPE_SOFTAP:
            hooks = OlaProGetSoftapHooks();
            break;
        default:
            OlaLogError("invalid netcfg protocol[%d]", cfgProtocol);
            return OLA_ERROR;
    }
    return RegisterProvisionHooks(hooks);
}

/* 配网流程总入口 */
int OlaProvisioningStart(void)
{
    if (!g_isInit) {
        OlaLogWarning("netcfg not init");
        return OLA_PROV_ERR_NETCFG_NOT_INIT;
    }
    bool needPro = false;
    if (IsNeedProvisioning(&needPro) != OLA_OK) {
        OlaLogInfo("check if device need provisioning failed");
        return OLA_ERROR;
    }

    /* 不需要配网则无需进行以下流程 */
    if (!needPro) {
        OlaLogInfo("dev no need provisioning");
        return OLA_OK;
    }
    if (ProvisioningHooksInit() != OLA_OK) {
        OlaLogError("provisioniong hooks init fail");
        UnRegisterProvisioningHooks();
        return OLA_ERROR;
    }
    if ((g_proHooks == NULL) || (g_proHooks->start == NULL)) {
        OlaLogError("provisioning hook null");
        UnRegisterProvisioningHooks();
        return OLA_ERROR;
    }
    if (g_proHooks->start() != OLA_OK) {
        OlaLogError("provisioning failed");
        UnRegisterProvisioningHooks();
        return OLA_ERROR;
    }
    return OLA_OK;
}

void OlaProvisioningStop(void)
{
    if ((g_proHooks == NULL) || (g_proHooks->stop == NULL)) {
        OlaLogError("provisioning hook null");
        return;
    }
    int ret = g_proHooks->stop();
    OlaLogInfo("provisioning stop, ret[%d]", ret);
    UnRegisterProvisioningHooks();
    return;
}

int OlaProvisioningInit(const OlaNetCfgParam *param)
{
    if (param == NULL) {
        OlaLogWarning("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    (void)memset_s(&g_netCfgParam, sizeof(OlaNetCfgParam), 0, sizeof(OlaNetCfgParam));
    if (memcpy_s(&g_netCfgParam, sizeof(OlaNetCfgParam), param, sizeof(OlaNetCfgParam)) != EOK) {
        OlaLogError("memcpy error");
        return OLA_SECUREC_ERR_MEMCOPY;
    }
    g_isInit = true;
    return OLA_OK;
}

const OlaNetCfgParam *OlaGetNetCfgParam(void)
{
    if (!g_isInit) {
        OlaLogWarning("netcfg not init");
        return NULL;
    }
    return &g_netCfgParam;
}